package cn.backpackerxl.easyJDBC.util;

import cn.backpackerxl.easyJDBC.annotation.Table;
import cn.backpackerxl.easyJDBC.main.EditTableImpl;
import cn.backpackerxl.easyJDBC.main.Generation;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

/**
 * @author: backpackerxl
 * @create: 2021/12/29
 * @filename: CreateTable
 **/
public final class CreateTable {
    private CreateTable() {
    }

    private static final String banner =
                    "                           _ _____  ____   _____ \n" +
                    "                          | |  __ \\|  _ \\ / ____|\n" +
                    "  ___  __ _ ___ _   _     | | |  | | |_) | |     \n" +
                    " / _ \\/ _` / __| | | |_   | | |  | |  _ <| |     \n" +
                    "|  __/ (_| \\__ \\ |_| | |__| | |__| | |_) | |____ \n" +
                    " \\___|\\__,_|___/\\__, |\\____/|_____/|____/ \\_____|\n" +
                    "  version=%s  __/ |                                \n" +
                    "                |___/                                 \n";

    private static final String version = "1.0.1";

    /**
     * 创建实体表格
     */
    public static void run() {
        System.out.printf(banner + "项目地址:https://gitee.com/backpackerxl/easy-jdbc", version);
        try {
            String packName;
            Properties properties = BasicProperty.get("easyJDBC.properties");
            packName = properties.getProperty("entity");
            Set<Class<?>> entities;
            if (packName != null) {
                entities = getClasses(packName);
            } else {
                throw new NullPointerException("no entity Configure");
            }
            for (Class<?> clazz : entities) {
                if (clazz.isAnnotationPresent(Table.class)) {
                    BeanUtil.setBean(clazz);
                    createTable(clazz);
                    if (clazz.getDeclaredAnnotation(Table.class).dev()) {
                        if (!SerializeField.exist(clazz.getName())) {
                            SerializeField.serialize(clazz.getName(), new FieldObject(clazz.getDeclaredFields()));
                        }
                        checkTable(clazz);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 检查表是否需要更新
     *
     * @param clazz
     */
    private static void checkTable(Class<?> clazz) {
        FieldObject deserialization = SerializeField.deserialization(clazz.getName());
        List<String> fie = Arrays.asList(deserialization.getFields());
        Map<String, Field> mapFields = ReflectUtil.getMapFields(clazz);
        Set<String> strings = mapFields.keySet();
        if (fie.size() > strings.size()) {
            EditTableImpl.doWithAlterTable(
                    clazz,
                    fie.stream().
                            filter(s -> !strings.contains(StringUtil.removeHump(s))).
                            collect(Collectors.toList())
            );
        } else if (fie.size() == strings.size()) {
            List<String> fil = Arrays.asList(ReflectUtil.getFields(clazz).getFields());
            Map<String, String> noSome = new HashMap<>();
            for (int i = 0; i < fil.size(); i++) {
                if (!fie.contains(fil.get(i))) {
                    noSome.put(fil.get(i), fie.get(i));
                }
            }
            EditTableImpl.doWithAlterTable(clazz, noSome, mapFields);
        } else {
            EditTableImpl.doWithAlterTable(
                    clazz,
                    strings.stream().
                            filter(s -> !fie.contains(StringUtil.addHump(s))).
                            collect(Collectors.toList()),
                    mapFields);
        }
        SerializeField.serialize(clazz.getName(), new FieldObject(clazz.getDeclaredFields()));
    }

    /**
     * 自动创建表格
     * <p>
     * 样本
     * <p>
     * create table if not exists t_admin1
     * (
     * id          int auto_increment,
     * name        varchar(100) not null,
     * create_time timestamp    not null,
     * constraint t_admin_pk primary key (id)
     * );
     *
     * @param clazz
     */
    private static void createTable(Class<?> clazz) {
        Generation.generationTable(clazz);
    }

    /**
     * 获取实体包下的所有类
     *
     * @param pack
     * @return
     */
    public static Set<Class<?>> getClasses(String pack) {
        Set<Class<?>> classes = new LinkedHashSet<>();
        String packageDirName = pack.replace(".", "/");
        boolean recursive = true;
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement(); // 获取下一个元素
                String protocol = url.getProtocol(); //获得协议名称
                if ("file".equals(protocol)) {
                    //获取包的物理路径
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    //以文件的方式扫描个包下文件并添加到集合中
                    findClassesInPackageByFile(pack, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    //jar包文件
                    //定义一个jarfile
                    System.out.println("jar类型的扫描");
                    JarFile jar;
                    try {
                        //获取jar
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        //从此jar包中得到一个枚举类
                        Enumeration<JarEntry> entries = jar.entries();
                        findClassesInPackageByJar(pack, entries, packageDirName, recursive, classes);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classes;
    }

    /**
     * 获取jar包下的用不到
     *
     * @param packageName
     * @param entries
     * @param packageDirName
     * @param recursive
     * @param classes
     */
    private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            if (String.valueOf(name.charAt(0)).equals("/")) {
                name = name.substring(1);
            }

            if (name.startsWith(packageDirName)) {
                int idx = name.lastIndexOf("/");
                if (idx != 1) {
                    packageName = name.substring(0, idx).replace("/", ".");
                }
                if (name.endsWith(".class") && !entry.isDirectory()) {
                    String className = name.substring(packageName.length() + 1, name.length() - 6);
                    try {
                        classes.add(Class.forName(packageName + "." + className));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 通过文件的形式获取
     *
     * @param packageName
     * @param filePath
     * @param recursive
     * @param classes
     */
    private static void findClassesInPackageByFile(String packageName, String filePath, final boolean recursive, Set<Class<?>> classes) {
        //获取此包的目录 建立一个File
        File dir = new File(filePath);
        //如果不存在或者也不是目录直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            System.out.println("实体表" + packageName + "下没有任何文件");
            return;
        }
        //如果存在
        File[] dirFiles = dir.listFiles(file -> (recursive && file.isDirectory()) || (file.getName().endsWith(".class")));

        assert dirFiles != null;
        for (File file : dirFiles) {
            if (file.isDirectory()) {
                findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
            } else {
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    //添加到集合中去
                    classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
